/**
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */

#include <cstdio>
#include <iostream>
#include <fstream>
#include "Bruinbase.h"
#include "SqlEngine.h"

using namespace std;

// external functions and variables for load file and sql command parsing 
extern FILE* sqlin;
int sqlparse(void);


RC SqlEngine::run(FILE* commandline)
{
  fprintf(stdout, "Bruinbase> ");

  // set the command line input and start parsing user input
  sqlin = commandline;
  sqlparse();  // sqlparse() is defined in SqlParser.tab.c generated from
               // SqlParser.y by bison (bison is GNU equivalent of yacc)

  return 0;
}

RC SqlEngine::select(int attr, const string& table, const vector<SelCond>& cond)
{
	
	// 1
  RecordFile rf;   // RecordFile containing the table
  RecordId   rid;  // record cursor for table scanning

	
BTreeIndex idx;
bool check_equal = false;


//2
  RC     rc;
  int    key;     
  string value;
  int    count;
  int    diff;

	int i_state; 





	i_state= 0;
	count = 0;
//3
  // open the table file
  if ((rc = rf.open(table + ".tbl", 'r')) < 0) {
    fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
    return rc;
  }
//
//
	SelCond* keyIs = NULL;
	SelCond* keyMax = NULL;
	SelCond* keyMin = NULL;
	SelCond* keyNe = NULL;


  //
	SelCond* valIs = NULL;
	SelCond* valMax = NULL;
	SelCond* valMin = NULL;
	SelCond* valNe = NULL;
	
	int temp;
	int keyGe = 0;
	int keyLe = 0;
	
	// fixed start
	
	for(unsigned i = 0; i < cond.size(); i++) 
	{
	temp = atoi(cond[i].value);
	switch(cond[i].attr) 
	{
		
	case 1:		
		switch (cond[i].comp) 
		{
		case SelCond::EQ:
			keyIs = (SelCond*) &cond[i];
			break;
		case SelCond::NE:
			check_equal = true;
			keyNe = (SelCond*) &cond[i];
			break;
		case SelCond::LE:
			temp++;
		case SelCond::LT:
			if (keyMax == NULL || atoi(keyMax->value) < temp) 
			{
			keyMax = (SelCond*) &cond[i];
				if(keyMax->comp == SelCond::LE) 
				{
				keyLe = 1;
				} 
				else 
				{
				keyLe = 0;
				}
			}
			break;
		case SelCond::GE:
			temp--;
		case SelCond::GT:
			if (keyMin == NULL || atoi(keyMin->value) > temp) 
			{
			keyMin = (SelCond*) &cond[i];
				if(keyMin->comp == SelCond::GE) 
				{
				keyGe = 1;
				} 
				else 
				{
				keyGe = 0;
				}
			}
			break;
		}
		break;
		
		case 2:
			switch (cond[i].comp) 
			{
			case SelCond::EQ:
				valIs = (SelCond*) &cond[i];
				break;
			case SelCond::NE:
				check_equal = true;
				valNe = (SelCond*) &cond[i];
				break;
			case SelCond::LE:
				temp++;
			case SelCond::LT:
				if (valMax == NULL || atoi(valMax->value) < temp)
					valMax = (SelCond*) &cond[i];
				break;
			case SelCond::GE:
				temp--;
			case SelCond::GT:
				if (valMin == NULL || atoi(valMin->value) > temp)
					valMin = (SelCond*) &cond[i];
				break;
			}
			break;
		}
	}
	
	if ((idx.open(table + ".idx", 'r')) == 0) 
	{
	i_state = 1;


	IndexCursor ic;
	
		if (keyIs || keyMin || keyMax || keyNe) 
		{
		int keyToFind;
		int keyLow;
		int keyHigh;
		int keyNot;
		if (keyIs)
			keyToFind = atoi(keyIs->value);
		else if(keyMin) 
		{
			keyLow = atoi(keyMin->value);
			keyToFind = keyLow;
		}

		if(keyMax) 
		{
			keyHigh = atoi(keyMax->value);
			if(! keyMin) 
			{
				keyToFind = keyHigh;
			}
		}

		if(keyNe) 
		{
			keyNot = atoi(keyNe->value);
		}

		if(keyMax) 
		{
			if((rc = idx.locate(0, ic)) != 0) 
			{
				idx.close();
				return rc;
			}
		} 
		else if(! keyNe)
		{
			if ((rc = idx.locate(keyToFind, ic)) != 0) 
			{
				idx.close();
				return rc;
			}
		}

		while (idx.readForward(ic, key, rid) == 0 ) 
		{		   			
		if(keyIs && key != keyToFind) continue;
		if(keyMin && ((keyGe==1)?(key < keyLow):(key <= keyLow))) continue;
		if(keyMax && ((keyLe==1)?(key > keyHigh):(key >= keyHigh))) break;
		if(keyNe && key == keyNot) continue;

		if ((rc = rf.read(rid, key, value)) != 0) 
		{
		fprintf(stderr, "Error: while reading a tuple from table %s\n", table.c_str());
		idx.close();
		return rc;
		}


		if (valIs != NULL && value != valIs->value)		continue;
		if (valMax != NULL &&  value >= valMax->value)	continue;
		if (valMin != NULL && value <= valMin->value)	continue;
		if (valNe && value <= valNe->value) continue;

		count++;

		switch (attr) 
		{
		case 1:  // SELECT key
			fprintf(stdout, "%d\n", key);
			break;
		case 2:  // SELECT value
			fprintf(stdout, "%s\n", value.c_str());
			break;
		case 3:  // SELECT *
			fprintf(stdout, "%d '%s'\n", key, value.c_str());
			break;
		}

		if(keyIs && key == keyToFind) break;
		if(keyNe && key != keyNot) break;

		}
		}

		if(keyIs || keyMin || keyMax || keyNe) 
		{
		if(attr == 4) 
		{
			fprintf(stdout, "%d\n", count);
			//fprintf(stdout, "case4%d\n", count);
		}
		}

		if(! (keyIs || keyMin || keyMax)) 
		{
			idx.close();
			goto tablescan;
		}

		idx.close();
  	}


	
	// fixed end
// check idx state
if(!i_state)
{
	tablescan:


//4
  // scan the table file from the beginning
  rid.pid = rid.sid = 0;
  count = 0;
  while (rid < rf.endRid()) {
    // read the tuple
    if ((rc = rf.read(rid, key, value)) < 0) {
      fprintf(stderr, "Error: while reading a tuple from table %s\n", table.c_str());
      goto exit_select;
    }

//fixed start

	if (keyIs != NULL && (int) key != atoi(keyIs->value))	goto next_tuple;
	if (keyMax != NULL && (int) key >= atoi(keyMax->value))	goto next_tuple;
	if (keyMin != NULL && (int) key <= atoi(keyMin->value))	goto next_tuple;
	if (keyNe && (int) key == atoi(keyNe->value)) goto next_tuple;

	if (valIs != NULL && value != valIs->value)		goto next_tuple;
	if (valMax != NULL &&  value >= valMax->value)	goto next_tuple;
	if (valMin != NULL && value <= valMin->value)	goto next_tuple;
	if (valNe && value == valNe->value) goto next_tuple;
//5
	// the condition is met for the tuple. 
	// increase matching tuple counter
	count++;

	// print the tuple
	switch (attr) 
	{
	case 1:  // SELECT key
		fprintf(stdout, "case1%d\n", key);
		break;
	case 2:  // SELECT value
		fprintf(stdout, "case2%s\n", value.c_str());
		break;
	case 3:  // SELECT *
		fprintf(stdout, "case3%d '%s'\n", key, value.c_str());
		break;
	}

	next_tuple:
	++rid;
	}

	// print matching tuple count if "select count(*)"
	if (attr == 4) {
		fprintf(stdout, "case4%d\n", count);
	}
	rc = 0;

	// close the table file and return
	exit_select:
	rf.close();
	}
	return rc;
//fixes end


}

RC SqlEngine::load(const string& table, const string& loadfile, bool index)
{
  /* your code here */
//
//
//
//
	// input file open
	fstream fs;
	const char *lf = loadfile.c_str();
	fs.open(lf);

	//create table
	RecordFile *rf = new RecordFile();
	RC rc=0;
	
	BTreeIndex idx;
	//create index file
	if(index)
	{
		idx.open(table + ".idx", 'w');
	}
	rc= rf->open(table + ".tbl", 'w');
	// check error for openinig file if not true return rc
	if( rc !=0)
	{
		fs.close();
		rf->close();
		return rc;
		
	}	
	//read input file util EOF and input data parse and append
	string line;
	string value;
	int key;
	
	while(getline(fs,line))
	{
		RecordId rid;
		rc = parseLoadLine(line, key, value);
		//error check for parsing
		if(rc!=0)
		{
			fs.close();
			rf->close();
			return rc;
		}
		//error check for appending
		rc = rf->append(key, value, rid);
		if(rc!=0)
		{
			fs.close();
			rf->close();
			return rc;
		}
		if(index)
		{
			rc = idx.insert(key, rid);

			if(rc != 0)
			{
				fs.close();
				rf->close();
				//idx->writeMetadate();
				idx.close();
				return rc;
			}
		}
		/*
			if(idx.insert(key, rid) != 0)
			{
			
				return RC_FILE_WRITE_FAILED;
			}
		}
*/
	}
	
	fs.close();
	rf->close();



  return 0;
}

RC SqlEngine::parseLoadLine(const string& line, int& key, string& value)
{
    const char *s;
    char        c;
    string::size_type loc;
    
    // ignore beginning white spaces
    c = *(s = line.c_str());
    while (c == ' ' || c == '\t') { c = *++s; }

    // get the integer key value
    key = atoi(s);

    // look for comma
    s = strchr(s, ',');
    if (s == NULL) { return RC_INVALID_FILE_FORMAT; }

    // ignore white spaces
    do { c = *++s; } while (c == ' ' || c == '\t');
    
    // if there is nothing left, set the value to empty string
    if (c == 0) { 
        value.erase();
        return 0;
    }

    // is the value field delimited by ' or "?
    if (c == '\'' || c == '"') {
        s++;
    } else {
        c = '\n';
    }

    // get the value string
    value.assign(s);
    loc = value.find(c, 0);
    if (loc != string::npos) { value.erase(loc); }

    return 0;
}
